﻿using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using POC.Logger;
using POC.Loggers.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace POC.Loggers.Storage
{
    public enum MessageFormat
    {
        Text = 1,
        Binary = 2,
        Xml = 4
    }
   public  class QueueStorageRespository: ICloudStorageRespository
    {
        AzureAccount _account;
        CloudQueue _queue;
        string _queueName;
        public MessageFormat _msgFormat = MessageFormat.Xml;
        private int maxMessages;

      
        public QueueStorageRespository(AzureAccount account, string queueName)
        {
            _account = account;
            _queueName = Regex.Replace(queueName, "[^a-zA-Z0-9]+", "", RegexOptions.Compiled).ToLower();
        }
        public QueueStorageRespository(AzureAccount account, string queueName, MessageFormat format = MessageFormat.Text)
        {
            _account = account;
            _queueName = Regex.Replace(queueName, "[^a-zA-Z0-9]+", "", RegexOptions.Compiled).ToLower(); 
            _msgFormat = format;
        }

        public void InitializeRepository()
        {          
            if (_queue == null)
            {  
                CloudQueueClient queueClient = _account.CloudAccount.CreateCloudQueueClient();
                _queue = queueClient.GetQueueReference(_queueName);
            }          
            _queue.CreateIfNotExists();
        }
        public Task PutEntry(LogEntry entry, bool lazyExecute)
        {
            Task task = null;
            if (_queue == null)
            {
                CloudQueueClient queueClient = _account.CloudAccount.CreateCloudQueueClient();
                _queue = queueClient.GetQueueReference(_queueName);
            }
            // TODO
            this.maxMessages = CloudQueueMessage.MaxNumberOfMessagesToPeek;

            CloudQueueMessage message = null;
            switch (_msgFormat)
            {
                case MessageFormat.Binary:
                    message = new CloudQueueMessage(entry.ToByteArray());
                    break;
                case MessageFormat.Text:
                    message = new CloudQueueMessage(entry.ToString());
                    break;
                case MessageFormat.Xml:
                    message = new CloudQueueMessage(entry.ToXml());
                    break;
            }
            if (lazyExecute)
            {
                task = Task.Factory.FromAsync(_queue.BeginAddMessage, _queue.EndAddMessage, message, null);
            }
            else
            {
                _queue.AddMessage(message); 
            }
            return task;
        }

        public byte[] GetMessage()
        {
            return this.GetMessage(true);
        }

        public byte [] GetMessage(bool delete)
        {
            CloudQueueMessage message = GetMessageFromQueue(delete);
            if (message == null) return null;
            return message.AsBytes;
        }

        public List<byte[]> GetMessages(bool delete)
        {
             List<byte[]> bufferMessages = new List<byte[]>();
            CloudQueueMessage[] messages = this.GetMessagesFromQueue(this.maxMessages, delete);
            if (messages == null || messages.Length <= 0) return bufferMessages;
            foreach (CloudQueueMessage message in messages)
            {
                bufferMessages.Add(message.AsBytes);
            }
            return bufferMessages;
        }

        public string GetPlainTextMessage()
        {
            return this.GetPlainTextMessage(true);
        }

        public string GetPlainTextMessage(bool delete)
        {
            CloudQueueMessage message = GetMessageFromQueue(delete);
            if (null != message)
            {
                if (_msgFormat == MessageFormat.Text || _msgFormat == MessageFormat.Xml)
                {
                    return message.InsertionTime.ToString() + ": " + message.AsString;
                }
                else
                {
                    LogEntry logEntry =message.AsBytes.ToLogEntry();
                    // TODO
                    return logEntry.ToString();
                }
            }
            return null;
        }

        public string[] GetPlainTextMessages()
        {
            return this.GetPlainTextMessages(true);
        }

        public string[] GetPlainTextMessages(bool delete)
        {
            List<string> stringMessages = new List<string>();
            CloudQueueMessage[] messages = this.GetMessagesFromQueue(this.maxMessages, delete);

            foreach (CloudQueueMessage message in messages)
            {
                if (_msgFormat == MessageFormat.Text || _msgFormat == MessageFormat.Xml)
                {
                    stringMessages.Insert(0, message.InsertionTime.ToString() + ": " + message.AsString);
                }
                else
                {
                    LogEntry logEntry = message.AsBytes.ToLogEntry();
                    // TODO      
                    stringMessages.Insert(0,logEntry.ToString());
                }
            }
            return stringMessages.ToArray();
        }

        public LogEntry GetLogEntry()
        {
            return this.GetLogEntry(true);
        }

        public LogEntry GetLogEntry(bool delete)
        {
            CloudQueueMessage message = GetMessageFromQueue(delete);
            if (null != message)
            {
                LogEntry entry = (_msgFormat == MessageFormat.Text || _msgFormat == MessageFormat.Xml) ?
                    message.AsString.FromXml() : message.AsBytes.ToLogEntry();
                return entry;
            }
            return null;
        }

        public LogEntry[] GetEntries()
        {
            return this.GetEntries(true);
        }

        public LogEntry[] GetEntries(bool delete)
        {
            List<LogEntry> logMessages = new List<LogEntry>();
            CloudQueueMessage[] messages = this.GetMessagesFromQueue(this.maxMessages, delete);

            foreach (CloudQueueMessage message in messages)
            {
                LogEntry entry = (_msgFormat == MessageFormat.Text || _msgFormat == MessageFormat.Xml) ?
                    message.AsString.FromXml() :
                    message.AsBytes.ToLogEntry();
                logMessages.Insert(0, entry);
            }
            return logMessages.ToArray();
        }

        private CloudQueueMessage GetMessageFromQueue(bool delete)
        {
            if (_queue == null)
                InitializeRepository();

            CloudQueueMessage message = null;
            try
            {
                message = _queue.GetMessage();
                if (null != message && delete)
                    _queue.DeleteMessage(message);
            }
            catch (StorageException storageClientException)
            {
                switch (storageClientException.RequestInformation.ExtendedErrorInformation.ErrorCode)
                {
                    case "MessageNotFound":
                        //deleted from a parallel app?
                        break;
                    case "PopReceiptMismatch":
                        break;
                    case "QueueDisabled":
                        break;
                    case "QueueNotFound":
                        break;
                    default:
                        throw;
                }
            }
            return message;
        }

        private CloudQueueMessage[] GetMessagesFromQueue(int maxMessages, bool delete)
        {
            if (_queue == null)
                InitializeRepository();

            CloudQueueMessage[] messages = null;
            try
            {
                maxMessages = maxMessages == 0 ? 1 : maxMessages;
                messages = _queue.GetMessages(maxMessages).ToArray<CloudQueueMessage>();
                if (delete && null != messages)
                {
                    foreach (CloudQueueMessage message in messages)
                    {
                        _queue.DeleteMessage(message);
                    }
                }
            }
            catch (StorageException storageClientException)
            {
                switch (storageClientException.RequestInformation.ExtendedErrorInformation.ErrorCode)
                {
                    case "OutOfRangeQueryParameterValue":
                        break;
                    case "MessageNotFound":
                        //deleted from a parallel app?
                        break;
                    case "PopReceiptMismatch":
                        break;
                    case "QueueDisabled":
                        break;
                    case "QueueNotFound":
                        break;
                    default:
                        throw;
                }
            }
            return messages;
        }

        public int MaxBatchSize
        {
            get { return this.maxMessages; }
            set { this.maxMessages = value; }
        }

        public int MessageCount
        {
            get
            {
                if (null != this._queue)
                {
                    _queue.FetchAttributes();
                    if (_queue.ApproximateMessageCount.HasValue)
                        return _queue.ApproximateMessageCount.Value;
                }
                return -1;
            }
        }
    }
}
